﻿/*
* Copyright (c) 2010-2012 Tesla Engine Group
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* 
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/


using System;
using Tesla.Core;
using Tesla.Graphics;
using Tesla.Graphics.Implementation;
using Tesla.Util;
using D3D = SlimDX.Direct3D10;
using SDX = SlimDX;

namespace Tesla.Direct3D10.Graphics.Implementation {
    /// <summary>
    /// Concrete Direct3D10 implementation for <see cref="VertexBuffer"/>.
    /// </summary>
    public sealed class D3D10VertexBufferImplementation : VertexBufferImplementation {
        private D3D10Renderer _renderer;
        private D3D.Device _graphicsDevice;
        private D3D.Buffer _buffer;
        private D3D.Buffer _staging;

        /// <summary>
        /// Gets the D3D10Renderer that created this resource.
        /// </summary>
        internal D3D10Renderer Renderer {
            get {
                return _renderer;
            }
        }

        /// <summary>
        /// Gets the D3D10 Vertex Buffer.
        /// </summary>
        internal D3D.Buffer D3DVertexBuffer {
            get {
                return _buffer;
            }
        }

        /// <summary>
        /// Creates a new instance of <see cref="D3D10VertexBufferImplementation"/>.
        /// </summary>
        /// <param name="renderer">The D3D10 renderer.</param>
        /// <param name="decl">The vertex declaration.</param>
        /// <param name="vertexCount">The vertex count.</param>
        /// <param name="usage">The resource usage.</param>
        /// <exception cref="Tesla.Core.TeslaException">Thrown if creating the underlying D3D10 buffer failed.</exception>
        internal D3D10VertexBufferImplementation(D3D10Renderer renderer, VertexDeclaration decl, int vertexCount, ResourceUsage usage)
            : base(decl, vertexCount, usage) {
            _renderer = renderer;
            _graphicsDevice = _renderer.GraphicsDevice;

            try {
                //Determine appropiate vertex buffer to create, let any exceptions bubble up.
                if(base.BufferUsage == ResourceUsage.Static) {
                    D3D.ResourceUsage rUsage = D3D.ResourceUsage.Default;
                    D3D.CpuAccessFlags cpuAccess = D3D.CpuAccessFlags.None;
                    _buffer = new D3D.Buffer(_graphicsDevice, new D3D.BufferDescription(vertexCount * decl.VertexStride, rUsage, D3D.BindFlags.VertexBuffer, cpuAccess, D3D.ResourceOptionFlags.None));
                } else if(base.BufferUsage == ResourceUsage.Dynamic) {
                    D3D.ResourceUsage rUsage = D3D.ResourceUsage.Dynamic;
                    D3D.CpuAccessFlags cpuAccess = D3D.CpuAccessFlags.Write;
                    _buffer = new D3D.Buffer(_graphicsDevice, new D3D.BufferDescription(vertexCount * decl.VertexStride, rUsage, D3D.BindFlags.VertexBuffer, cpuAccess, D3D.ResourceOptionFlags.None));
                }

                //Add to tracker
                _renderer.Resources.AddTrackedObject(_buffer.ComPointer, this);
            } catch(Exception e) {
                Dispose();
                throw new TeslaException("Error creating D3D10 buffer: \n" + e.Message, e);
            }
        }

        /// <summary>
        /// Creates a new instance of <see cref="D3D10VertexBufferImplementation"/> initialized with the interleaved data.
        /// </summary>
        /// <param name="renderer">The D3D10 renderer.</param>
        /// <param name="decl">The vertex declaration.</param>
        /// <param name="usage">The resource usage.</param>
        /// <param name="data">The interleaved data.</param>
        /// <exception cref="Tesla.Core.TeslaException">Thrown if creating the underlying D3D10 buffer or writing to it failed.</exception>
        internal D3D10VertexBufferImplementation(D3D10Renderer renderer, VertexDeclaration decl, ResourceUsage usage, DataBuffer data)
            : base(decl, usage, data) {
            _renderer = renderer;
            _graphicsDevice = _renderer.GraphicsDevice;

            //Determine appropiate buffer, let any exceptions bubble up.
            try {
                using(SDX.DataStream ds = new SDX.DataStream(data.ByteDataCopy, true, false)) {
                    //Now create and populate appropiate D3D10 buffer
                    if(base.BufferUsage == ResourceUsage.Static) {
                        D3D.ResourceUsage rUsage = D3D.ResourceUsage.Default;
                        D3D.CpuAccessFlags cpuAccess = D3D.CpuAccessFlags.None;
                        _buffer = new D3D.Buffer(_graphicsDevice, ds, new D3D.BufferDescription(data.SizeInBytes, rUsage, D3D.BindFlags.VertexBuffer, cpuAccess, D3D.ResourceOptionFlags.None));
                    } else if(base.BufferUsage == ResourceUsage.Dynamic) {
                        D3D.ResourceUsage rUsage = D3D.ResourceUsage.Dynamic;
                        D3D.CpuAccessFlags cpuAccess = D3D.CpuAccessFlags.Write;
                        _buffer = new D3D.Buffer(_graphicsDevice, ds, new D3D.BufferDescription(data.SizeInBytes, rUsage, D3D.BindFlags.VertexBuffer, cpuAccess, D3D.ResourceOptionFlags.None));
                    }
                }

                //Add to tracker
                _renderer.Resources.AddTrackedObject(_buffer.ComPointer, this);
            } catch(Exception e) {
                Dispose();
                throw new TeslaException("Error creating D3D10 buffer: \n" + e.Message, e);
            }
        }

        /// <summary>
        /// Creates a new instance of <see cref="D3D10VertexBufferImplementation"/> initialized with the vertex data array.
        /// </summary>
        /// <param name="renderer">The D3D10 renderer.</param>
        /// <param name="decl">The vertex declaration.</param>
        /// <param name="usage">The resource usage.</param>
        /// <param name="data">The array of vertex data.</param>
        /// <exception cref="Tesla.Core.TeslaException">Thrown if creating the underlying D3D10 buffer or writing to it failed.</exception>
        internal D3D10VertexBufferImplementation(D3D10Renderer renderer, VertexDeclaration decl, ResourceUsage usage, params DataBuffer[] data)
            : base(decl, usage, data) {
                _renderer = renderer;
                _graphicsDevice = renderer.GraphicsDevice;

                int totalSizeInBytes = base.VertexCount * decl.VertexStride;
                int vertexStride = decl.VertexStride;

                try {
                    //Now initialize the buffer with the supplied vertex store
                    using(SDX.DataStream interleaved = new SDX.DataStream(totalSizeInBytes, true, true)) {
                        //Create the interleaved buffer
                        byte[] vertex = new byte[vertexStride];
                        for(int i = 0; i < base.VertexCount; i++) {
                            int offset = 0;
                            for(int j = 0; j < data.Length; j++) {
                                DataBuffer db = data[j];
                                int elementSize = db.ElementSizeInBytes;
                                db.Get(vertex, offset, elementSize);
                                offset += elementSize;
                            }
                            interleaved.Write(vertex, 0, vertexStride);
                        }
                        interleaved.Position = 0;
                        //Now create and populate appropiate D3D10 buffer
                        if(base.BufferUsage == ResourceUsage.Static) {
                            D3D.ResourceUsage rUsage = D3D.ResourceUsage.Default;
                            D3D.CpuAccessFlags cpuAccess = D3D.CpuAccessFlags.None;
                            _buffer = new D3D.Buffer(_graphicsDevice, interleaved, new D3D.BufferDescription(totalSizeInBytes, rUsage, D3D.BindFlags.VertexBuffer, cpuAccess, D3D.ResourceOptionFlags.None));
                        } else if(base.BufferUsage == ResourceUsage.Dynamic) {
                            D3D.ResourceUsage rUsage = D3D.ResourceUsage.Dynamic;
                            D3D.CpuAccessFlags cpuAccess = D3D.CpuAccessFlags.Write;
                            _buffer = new D3D.Buffer(_graphicsDevice, interleaved, new D3D.BufferDescription(totalSizeInBytes, rUsage, D3D.BindFlags.VertexBuffer, cpuAccess, D3D.ResourceOptionFlags.None));
                        }
                    }

                    //Add to tracker
                    _renderer.Resources.AddTrackedObject(_buffer.ComPointer, this);
                } catch(Exception e) {
                    Dispose();
                    throw new TeslaException("Error creating D3D10 buffer: \n" + e.Message, e);
                }
        }

        /// <summary>
        /// Releases unmanaged resources and performs other cleanup operations before the
        /// <see cref="D3D10VertexBufferImplementation"/> is reclaimed by garbage collection.
        /// </summary>
        ~D3D10VertexBufferImplementation() {
            Dispose(false);
        }

        /// <summary>
        /// Convienence method that takes an array of data buffers, each representing a vertex element (in the order
        /// declared by the vertex declaration), and writes all of the data to the vertex buffer. The buffers must match
        /// the vertex declaration as well as the byte sizes of each element and all be of the same length.
        /// </summary>
        /// <param name="data">Array of databuffers representing the vertex data.</param>
        /// <exception cref="System.ArgumentNullException">Thrown if data is null.</exception>
        /// <exception cref="System.ArgumentOutOfRangeException">Thrown if the number of buffers do not match the number
        /// of vertex elements, or if the number of vertices in each buffer does not match the vertex count,
        /// or if there is a byte size mismatch of any kind.</exception>
        public override void SetInterleavedData(params DataBuffer[] data) {
            if(_buffer == null || _buffer.Disposed) {
                throw new ObjectDisposedException(GetType().Name);
            }

            if(data == null || data.Length == 0) {
                throw new ArgumentNullException("data", "Data cannot be null.");
            }

            VertexDeclaration vertexDecl = base.VertexDeclaration;
            int vertexCount = base.VertexCount;

            //Verify if the incoming vertex streams match right with the supplied vertex declaration
            VertexElement[] elems = vertexDecl.VertexElements;
            if(elems.Length != data.Length) {
                throw new ArgumentOutOfRangeException("data", "Number of vertex streams do not match up the number of declared vertex elements.");
            }

            int totalSizeInBytes = 0;
            int vertexStride = 0;

            for(int i = 0; i < data.Length; i++) {
                DataBuffer db = data[i];
                VertexElement element = elems[i];
                int vSizeInBytes = db.ElementSizeInBytes;
                int vCount = db.SizeInBytes / vSizeInBytes;

                if(vCount != vertexCount) {
                    throw new ArgumentOutOfRangeException("data", "Vertex count mismatch, buffers must be of same length.");
                } 
                if(vSizeInBytes != VertexDeclaration.GetVertexElementSize(element.Format)) {
                    throw new ArgumentOutOfRangeException("data", "Supplied vertex buffer element size mismatch with actual vertex element size.");
                }

                totalSizeInBytes += db.SizeInBytes;
                vertexStride += vSizeInBytes;
                db.Position = 0;
            }

            if(totalSizeInBytes != vertexDecl.VertexStride * vertexCount) {
                throw new ArgumentOutOfRangeException("data", "Vertex data must match the size of the vertex buffer in bytes!");
            }

            CreateStaging();

            try {
                using(SDX.DataStream interleaved = _staging.Map(D3D.MapMode.Write, D3D.MapFlags.None)) {
                    byte[] vertex = new byte[vertexStride];
                    for(int i = 0; i < vertexCount; i++) {
                        int startIndex = 0;
                        for(int j = 0; j < data.Length; j++) {
                            DataBuffer db = data[j];
                            int elementSize = db.ElementSizeInBytes;
                            db.Get(vertex, startIndex, elementSize);
                            startIndex += elementSize;
                        }
                        interleaved.Write(vertex, 0, vertexStride);
                    }
                    _staging.Unmap();
                    //Copy entire resource
                    _graphicsDevice.CopyResource(_staging, _buffer);
                }
            } catch(Exception e) {
                throw new TeslaException("Error writing to D3D10 Buffer.", e);
            }
        }

        /// <summary>
        /// Sets the vertex data from an array source.
        /// </summary>
        /// <typeparam name="T">The type of data in the vertex buffer.</typeparam>
        /// <param name="data">Array that holds the vertex data</param>
        /// <param name="startIndex">Starting index of the element in the array at which to start copying from</param>
        /// <param name="elementCount">Number of elements to copy from the array</param>
        /// <param name="offsetInBytes">Offset in bytes from the beginning of the vertex buffer to the data.</param>
        /// <param name="vertexStride">Size of an element in bytes.</param>
        /// <param name="writeOptions">Writing options for the vertex buffer. None, discard, or no overwrite.</param>
        /// <exception cref="System.ObjectDisposedException">Thrown if Dispose() has been called.</exception>
        /// <exception cref="System.InvalidOperationException">Thrown if the write options are incompatible with the resource usage of the buffer.</exception>
        /// <exception cref="System.ArgumentOutOfRangeException">Thrown if the data's vertex stride is too small, the offset in bytes is out of range,
        /// or the byte offset and number of elements to write will cause overflow.</exception>
        /// <exception cref="Tesla.Core.TeslaException">Thrown if there was an error writing to the buffer.</exception>
        public override void SetData<T>(T[] data, int startIndex, int elementCount, int offsetInBytes, int vertexStride, DataWriteOptions writeOptions) {
            if(_buffer == null || _buffer.Disposed) {
                throw new ObjectDisposedException(GetType().Name);
            }

            //Throw an error if an invalid write options is specified
            if(base.BufferUsage == ResourceUsage.Static && writeOptions != DataWriteOptions.None) {
                throw new InvalidOperationException("Can only specify write options other than DataWriteOptions.None for dynamic vertex buffers.");
            }

            //Check if array bounds are out of range
            D3D10Helper.CheckArrayBounds(data, startIndex, elementCount);

            VertexDeclaration vertexDecl = base.VertexDeclaration;
            int vertexCount = base.VertexCount;

            int vbSize = vertexCount * vertexDecl.VertexStride;
            int elemSize = MemoryHelper.SizeOf<T>();
            int dataSize = elementCount * elemSize;
            int vertexStep = vertexStride;
            if(vertexStride != 0) {
                vertexStep -= elemSize;
                if(vertexStep < 0) {
                    throw new ArgumentOutOfRangeException("vertexStride", "Vertex stride is too small for requested data size.");
                }
                //If we get this far, we need to make sure the actual bytes we're going to look at matches up,
                //since we can grab specific parts of a vertex and not the whole thing
                if(elementCount > 1) {
                    dataSize = ((elementCount - 1) * vertexStep) + dataSize;
                }
            }

            //Prevent overflow out of range errors
            if((offsetInBytes < 0) || (offsetInBytes > vbSize)) {
                throw new ArgumentOutOfRangeException("offsetInbytes", "Byte offset is out of range.");
            }

            if((offsetInBytes + dataSize) > vbSize) {
                throw new ArgumentOutOfRangeException("data", "Byte offset and elements to write will cause in buffer overflow.");
            }

            //Create scratch buffer, if it hasn't been created already
            bool usesStaging = false;
            if(base.BufferUsage == ResourceUsage.Static || writeOptions == DataWriteOptions.None) {
                CreateStaging();
                usesStaging = true;
            }

            try {
                if(usesStaging) {
                    //If we're not going to be writing the entire vertex structure, we need to first
                    //copy the contents of the affected vertex data into the staging buffer
                    if(vertexStep != vertexStride) {
                        //If we're going to be working with all the verts, no need to copy a subresource region
                        if(offsetInBytes == 0 && startIndex == 0 && vertexCount == data.Length) {
                            _graphicsDevice.CopyResource(_buffer, _staging);
                        } else {
                            D3D.ResourceRegion region = new D3D.ResourceRegion();
                            region.Left = offsetInBytes;
                            region.Right = offsetInBytes + dataSize;
                            region.Front = region.Top = 0;
                            region.Back = region.Bottom = 1;
                            _graphicsDevice.CopySubresourceRegion(_buffer, 0, region, _staging, 0, offsetInBytes, 0, 0);
                        }
                    }

                    using(SDX.DataStream ds = _staging.Map(D3D.MapMode.Read, D3D.MapFlags.None)) {
                        //If the step is zero, that means we're dealing with the entire vertex and not a subset of it
                        if(vertexStep == 0) {
                            ds.Position = offsetInBytes;
                            ds.WriteRange<T>(data, startIndex, elementCount);
                        } else {
                            ds.Position = offsetInBytes;
                            int count = elementCount - 1;
                            int index = startIndex;
                            ds.Write<T>(data[index++]);
                            while(count > 0) {
                                ds.Position += vertexStep;
                                ds.Write<T>(data[index]);
                                count--;
                                index++;
                            }
                        }
                        _staging.Unmap();

                        //If we're writing to the entire VB just copy the whole thing
                        if(offsetInBytes == 0 && startIndex == 0 && dataSize == vbSize) {
                            _graphicsDevice.CopyResource(_staging, _buffer);
                        } else {
                            D3D.ResourceRegion region = new D3D.ResourceRegion();
                            region.Left = offsetInBytes;
                            region.Right = offsetInBytes + dataSize;
                            region.Front = region.Top = 0;
                            region.Back = region.Bottom = 1;
                            _graphicsDevice.CopySubresourceRegion(_staging, 0, region, _buffer, 0, offsetInBytes, 0, 0);
                        }
                    }
                //Dynamic vertex buffers only
                } else {
                    D3D.MapMode mode = (writeOptions == DataWriteOptions.Discard) ? D3D.MapMode.WriteDiscard : D3D.MapMode.WriteNoOverwrite;
                    using(SDX.DataStream ds = _buffer.Map(mode, D3D.MapFlags.None)) {
                        //If the step is zero, that means we're dealing with the entire vertex and not a subset of it
                        if(vertexStep == 0) {
                            ds.Position = offsetInBytes;
                            ds.WriteRange<T>(data, startIndex, elementCount);
                        } else {
                            ds.Position = offsetInBytes;
                            int count = elementCount - 1;
                            int index = startIndex;
                            ds.Write<T>(data[index++]);
                            while(count > 0) {
                                ds.Position += vertexStep;
                                ds.Write<T>(data[index]);
                                count--;
                                index++;
                            }
                        }
                        _buffer.Unmap();
                    }
                }
            } catch(Exception e) {
                throw new TeslaException("Error reading from D3D10 Buffer.", e);
            }
        }



        /// <summary>
        /// Gets the data from the vertex buffer and copies it into specified array.
        /// </summary>
        /// <typeparam name="T">The type of data in the vertex buffer.</typeparam>
        /// <param name="data">Array to copy contents to from the vertex buffer into</param>
        /// <param name="startIndex">Index of the element in the array at each to start writing to</param>
        /// <param name="elementCount">Number of elements to copy</param>
        /// <param name="offsetInBytes">Offset in bytes from the beginning of the vertex buffer to the data.</param>
        /// <param name="vertexStride">Size of an element in bytes</param>
        /// <exception cref="System.ObjectDisposedException">Thrown if Dispose() has been called.</exception>
        /// <exception cref="System.InvalidOperationException">Thrown if the write options are incompatible with the resource usage of the buffer.</exception>
        /// <exception cref="System.ArgumentOutOfRangeException">Thrown if the data's vertex stride is too small, the offset in bytes is out of range,
        /// or the byte offset and number of elements to read will cause overflow.</exception>
        /// <exception cref="Tesla.Core.TeslaException">Thrown if there was an error reading from the buffer.</exception>
        public override void GetData<T>(T[] data, int startIndex, int elementCount, int offsetInBytes, int vertexStride) {
            if(_buffer == null || _buffer.Disposed) {
                throw new ObjectDisposedException(GetType().Name);
            }

            //Check if array bounds are out of range
            D3D10Helper.CheckArrayBounds(data, startIndex, elementCount);

            VertexDeclaration vertexDecl = base.VertexDeclaration;
            int vertexCount = base.VertexCount;

            int vbSize = vertexCount * vertexDecl.VertexStride;
            int elemSize = MemoryHelper.SizeOf<T>();
            int dataSize = elementCount * elemSize;
            int vertexStep = vertexStride;
            if(vertexStride != 0) {
                vertexStep -= elemSize;
                if(vertexStep < 0) {
                    throw new ArgumentException("vertexStride", "Vertex stride is too small for requested data size.");
                }
                //If we get this far, we need to make sure the actual bytes we're going to look at matches up,
                //since we can grab specific parts of a vertex and not the whole thing
                if(elementCount > 1) {
                    dataSize = ((elementCount - 1) * vertexStep) + dataSize;
                }
            } 

            //Prevent overflow out of range errors
            if((offsetInBytes < 0) || (offsetInBytes > vbSize)) {
                throw new ArgumentOutOfRangeException("offsetInBytes", "Byte offset is out of range.");
            }

            if((offsetInBytes + dataSize) > vbSize) {
                throw new ArgumentOutOfRangeException("data", "Byte offset and elements to read will cause in buffer overflow.");
            }

            //Create scratch buffer, if it hasn't been created already
            CreateStaging();

            try {
                //If we're reading from the entire VB just copy the whole thing
                if(offsetInBytes == 0 && startIndex == 0 && vertexCount == data.Length) {
                    _graphicsDevice.CopyResource(_buffer, _staging);
                } else {
                    D3D.ResourceRegion region = new D3D.ResourceRegion();
                    region.Left = offsetInBytes;
                    region.Right = offsetInBytes + dataSize;
                    region.Front = region.Top = 0;
                    region.Back = region.Bottom = 1;
                    _graphicsDevice.CopySubresourceRegion(_buffer, 0, region, _staging, 0, offsetInBytes, 0, 0);
                }

                using(SDX.DataStream ds = _staging.Map(D3D.MapMode.Read, D3D.MapFlags.None)) {
                    //If the step is zero, that means we're dealing with the entire vertex and not a subset of it
                    if(vertexStep == 0) {
                        ds.Position = offsetInBytes;
                        ds.ReadRange<T>(data, startIndex, elementCount);
                    } else {
                        ds.Position = offsetInBytes;
                        int count = elementCount - 1;
                        int index = startIndex;
                        data[index++] = ds.Read<T>();
                        while(count > 0) {
                            ds.Position += vertexStep;
                            data[index] = ds.Read<T>();
                            count--;
                            index++;
                        }
                    }
                    _staging.Unmap();
                }
            } catch(Exception e) {
                throw new TeslaException("Error reading from D3D10 Buffer.", e);
            }
        }

        private void CreateStaging() {
            if(_staging == null) {
                D3D.BufferDescription desc = new D3D.BufferDescription();
                desc.BindFlags = D3D.BindFlags.None;
                desc.CpuAccessFlags = D3D.CpuAccessFlags.Write | D3D.CpuAccessFlags.Read;
                desc.OptionFlags = D3D.ResourceOptionFlags.None;
                desc.SizeInBytes = base.VertexCount * base.VertexDeclaration.VertexStride;
                desc.Usage = D3D.ResourceUsage.Staging;
                _staging = new D3D.Buffer(_graphicsDevice, desc);

                //Add staging
                _renderer.Resources.AddTrackedObject(_staging.ComPointer, this);
            }
        }

        /// <summary>
        /// Releases unmanaged and - optionally - managed resources
        /// </summary>
        /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
        protected override void Dispose(bool disposing) {
            if(!IsDisposed) {
                //Dispose of managed resources
                if(disposing) {
                    if(_buffer != null) {
                        if(_renderer != null) {
                            _renderer.Resources.RemoveTrackedObject(_buffer.ComPointer);
                        }
                        _buffer.Dispose();
                        _buffer = null;
                    }
                    if(_staging != null) {
                        if(_renderer != null) {
                            _renderer.Resources.RemoveTrackedObject(_staging.ComPointer);
                        }
                        _staging.Dispose();
                        _staging = null;
                    }
                }
                base.Dispose(disposing);
            }
        }
    }
}
